#include "cinder/app/AppBasic.h"
#include <vld.h>
#include "cinder/params/Params.h"
#include "cinder/Camera.h"


#include "util/Shared_ptr.h"
#include "ProcessManager.h"
#include "event/Events.h"
#include "Messenger.h"
#include "Rigidbody.h"
#include "ColliderSphere.h"
#include "ColliderBox.h"
#include "Quaternion.h"


#include "rendering/IObserverRendering.h"
#include "DebugInfoNode.h"
#include "Event_Control_Changed.h"
#include "CompCommandListener.h"
#include "PhysicsRenderer.h"
#include "SpriteAnimation.h"
#include "PlayerController.h"
#include "DonkeyKongController.h"
#include "Event_DK_Control_Changed.h"
#include "DKCommandListener.h"



using namespace ci;
using namespace ci::app;
using namespace std;
using namespace Boon;
using namespace Rendering;



class ClearScene : public Boon::Rendering::IObserverRendering
{
public:
	virtual void OnPrerender( const InfoObserverRendering& i ) { gl::clear(); }
	virtual void OnRender( const InfoObserverRendering& i )
	{	/*params::InterfaceGl::draw();*/}
	virtual void OnPostRender( const InfoObserverRendering& i ) {}
};

class DonkeyKongMain : public AppBasic
{
public:

	void					prepareSettings( Settings *settings );
	void					setup();
	void					mouseDown( MouseEvent event );	
	void					keyDown( KeyEvent event );
	void					keyUp( KeyEvent event );
	void					update();
	void					draw();
	void					shutdown();
	void					resize( ResizeEvent event );
	void					CreateGround( const Vector3& position, const Vector3& size, float rotation );
	void					CreateMario();
	void					CreateDonkeyKong();

private:
	params::InterfaceGl					m_Params;
	ci::CameraOrtho						m_Cam;
	Vec3f								m_Pos;
	float								rotation;

	ProcessManager*							m_Pm;
	Shared_ptr<Event_Control_Changed>		m_ControlEvent;
	Shared_ptr<Event_DK_Control_Changed>	m_DKControlEvent;
	Shared_ptr<ClearScene>					m_Clearer;
	GameObjectWeak							ground, texture;
	GameObject*								p;
};


void DonkeyKongMain::prepareSettings( Settings *settings )
{
#ifdef _DEBUG
	FILE* f;
	AllocConsole();
	freopen_s( &f, "CON", "w", stdout );
	settings->setTitle("Donkey Kong | by Robert Sommeregger");
#endif
}

HWND gHWND;
HWND GetHWND()
{
	return gHWND;
}

void DonkeyKongMain::setup()
{
	gl::enableAlphaBlending();
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	m_Params = params::InterfaceGl( "App parameters", Vec2i( 0, 100 ) );
	m_Params.addParam( "a", &m_Pos, "" );
	m_Params.addParam( "rot", &rotation, "" );

	gHWND = getRenderer()->getHwnd();

	m_ControlEvent = Shared_ptr<Event_Control_Changed>( new Event_Control_Changed() );
	m_DKControlEvent = Shared_ptr<Event_DK_Control_Changed>( new Event_DK_Control_Changed() );

	m_Pm = &ProcessManager::GetInstance();
	m_Clearer = Shared_ptr<ClearScene>( new ClearScene() );
	m_Pm->GetRootNode()->RegisterObserver( &m_Clearer ); // render observer on root to clear scene on drawing

	try
	{
		// walking ground
		CreateGround( Vector3( 50, 2, 0 ), Vector3( 100, 2, 5 ), 0 );
		CreateGround( Vector3( 25, 18, 0 ), Vector3( 100, 2, 5 ), -4 );
		CreateGround( Vector3( 75, 36, 0 ), Vector3( 100, 2, 5 ), 4 );
		CreateGround( Vector3( 25, 53, 0 ), Vector3( 80, 2, 5 ), -4 );
		CreateGround( Vector3( 75, 68, 0 ), Vector3( 100, 2, 5 ), 4 );
		CreateGround( Vector3( 40, 80, 0 ), Vector3( 80, 2, 5 ), 0 );

		//side
		CreateGround( Vector3( -1, 50, 0 ), Vector3( 1, 80, 5 ), 0 );
		CreateGround( Vector3( 101, 50, 0 ), Vector3( 1, 100, 5 ), 0 );

		texture = GameObject::Create( "Box" );
		texture.Lock()->GetTransform().SetPosition( Vector3( 50, 30, 0 ) );
		texture.Lock()->AddComponent<Rigidbody>();
		texture.Lock()->AddComponent<ColliderSphere>().Lock()->SetRadius( 1 );
		texture.Lock()->AddComponent<PhysicsRenderer>();

		CreateMario();
		CreateDonkeyKong();
	}
	catch (std::exception& e)
	{
		const char* sadfla = e.what();
		const char* blubb = sadfla;
		int bla = 3;
	}
}

void DonkeyKongMain::CreateGround( const Vector3& position, const Vector3& size, float rotation )
{
	GameObject& ground = *GameObject::Create( "ground" ).Lock().Get();
	ground.GetTransform().SetPosition( position );
	ground.GetTransform().Rotate( rotation );
	ground.AddComponent<ColliderBox>().Lock()->SetSize( size );
	ground.AddComponent<PhysicsRenderer>();
}


void DonkeyKongMain::resize( ResizeEvent event )
{

}

void DonkeyKongMain::mouseDown( MouseEvent event )
{
}

void DonkeyKongMain::keyDown( KeyEvent event )
{
	if( event.getChar() == 's' )
	{
		ostringstream oss;
		oss << "Le_Box_" << Boon::Timer::TimeStamp();
		std::cout << "created " << oss.str().c_str() << std::endl;

		texture = GameObject::Create( oss.str().c_str() );
		texture.Lock()->GetTransform().SetPosition( Vector3( 50, 90, 0 ) );
		texture.Lock()->AddComponent<Rigidbody>().Lock()->SetConstraints( RIGC_FREEZE_POS_Z | RIGC_FREEZE_ROT_X | RIGC_FREEZE_ROT_Y, true );
		texture.Lock()->AddComponent<ColliderSphere>().Lock()->SetRadius( 3 );
		texture.Lock()->AddComponent<PhysicsRenderer>();
		texture.Lock()->AddComponent<SpriteAnimation>().Lock()->GetMaterial().SetBounds( Bounds( Vector3::ZERO, Vector3::ONE * 6 ));
		SpriteAnimation& sa = *texture.Lock()->GetComponentBase<SpriteAnimation>().Lock().Get();
		sa.GetMaterial().SetImagePath( "data/sprite/objects.png" );
		sa.CreateClip( "Barrel", Vector2( 4, 1692 ), Vector2( 36, 45 ), 8, 12, WM_LOOP );
		sa.Play( "Barrel" );
		texture.Lock()->GetComponent<Rigidbody>().Lock()->AddForce( 8, 0, 0, FM_IMPULSE );
		//texture.Lock()->GetComponent<ColliderBox>().Lock()->Register( CompCommandListener::COMPONENT_ID, cs );
	}
	else if( event.getChar() == 't' )
	{
		/*texture.Lock()->GetCollider()->SetTrigger( !texture.Lock()->GetCollider()->IsTrigger() );
		std::cout << "istrigger: " << texture.Lock()->GetCollider()->IsTrigger() << std::endl;*/
	}
}


void DonkeyKongMain::keyUp( KeyEvent event )
{

}

#define PrintVec( vec ) (std::cout << "Vec3: x: " << (vec).x << " y: " << (vec).y << " z: " << (vec).z << std::endl)


void DonkeyKongMain::update()
{
	Messenger::GetInstance().Bang( m_DKControlEvent );
	m_Pm->ProcessUpdate();
	Messenger::GetInstance().Bang( m_ControlEvent );
	//std::cout << "tex"  << std::endl; PrintVec( texture.Lock()->GetTransform().GetPosition() );
	//std::cout << "ground"  << std::endl; PrintVec( ground.Lock()->GetTransform().GetPosition() );
}

void DonkeyKongMain::draw()
{

	//m_Cam.setOrtho( 0, 200, 0, 200, -1, 1 );

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity(); 
	glOrtho( 0, 100, 0, 100, -5, 5 ); 
	glMatrixMode(GL_MODELVIEW);        
	glLoadIdentity();

	m_Pm->ProcessRendering();
}

void DonkeyKongMain::shutdown()
{
	m_Pm->Release();
}

void DonkeyKongMain::CreateMario()
{
	GameObject& player = *GameObject::Create( "Mario" ).Lock().Get();
	player.GetTransform().SetPosition( 5, 4, 0 );
	player.AddComponent<PlayerController>();
	player.AddComponent<CompCommandListener>();
	player.AddComponent<Rigidbody>().Lock()->SetConstraints( RIGC_FREEZE_POS_Z | RIGC_FREEZE_ROT_X | RIGC_FREEZE_ROT_Y | RIGC_FREEZE_ROT_Z, true );
	player.AddComponent<PhysicsRenderer>();
	player.AddComponent<ColliderSphere>().Lock()->SetRadius( 2.5f );
	player.AddComponent<SpriteAnimation>().Lock()->GetMaterial().SetBounds( Bounds( Vector3::ZERO, Vector3::ONE * 6.0f ));

	SpriteAnimation& sa = *player.GetComponentBase<SpriteAnimation>().Lock().Get();
	sa.GetMaterial().SetImagePath( "data/sprite/mario.png" );
	sa.CreateClip( "Run", Vector2( 80, 130 ), Vector2( 25, 30 ), 3, 12, WM_LOOP );
	sa.CreateClip( "Idle", Vector2( 174, 130 ), Vector2( 20, 30 ), 1, 12, WM_LOOP );
	sa.CreateClip( "AttackFlip", Vector2( 174, 130 ), Vector2( 22, 30 ), 3, 14, WM_ONCE, 3 );
	sa.CreateClip( "Jump", Vector2( 242, 130 ), Vector2( 25, 30 ), 3, 12, WM_CLAMP, 1 );
	sa.CreateClip( "DoubleJump", Vector2( 319, 130 ), Vector2( 27, 31 ), 7, 12, WM_CLAMP, 2 );
	sa.Play( "Idle" );
}

void DonkeyKongMain::CreateDonkeyKong()
{
	GameObject& player = *GameObject::Create( "DK" ).Lock().Get();
	player.AddComponent<DonkeyKongController>();
	player.AddComponent<DKCommandListener>();
	player.GetTransform().SetPosition( 10, 87, 0 );
	player.AddComponent<PhysicsRenderer>();
	player.AddComponent<ColliderSphere>().Lock()->SetRadius( 8.0f );
	player.GetCollider()->SetTrigger( true );
	player.AddComponent<SpriteAnimation>().Lock()->GetMaterial().SetBounds( Bounds( Vector3::UP * 1.7f, Vector3::ONE * 15 ));

	SpriteAnimation& sa = *player.GetComponentBase<SpriteAnimation>().Lock().Get();
	sa.GetMaterial().SetImagePath( "data/sprite/donkeykong.png" );
	sa.CreateClip( "FacePalm", Vector2( 4, 1090 ), Vector2( 41, 50 ), 12, 12, WM_ONCE );
	sa.CreateClip( "IamBest", Vector2( 5, 1277 ), Vector2( 53, 62 ), 12, 12, WM_ONCE );
	sa.CreateClip( "Defeated", Vector2( 0, 544 ), Vector2( 58, 68 ), 18, 12, WM_CLAMP );
	sa.CreateClip( "Attack", Vector2( 6, 691 ), Vector2( 57, 74 ), 19, 16, WM_ONCE );
	sa.CreateClip( "Idle", Vector2( 7, 22 ), Vector2( 43, 48 ), 11, 12, WM_LOOP );
	sa.CreateClip( "Win", Vector2( 1, 1019 ), Vector2( 41, 68 ), 37, 12, WM_LOOP );
	sa.Play( "Idle" );
}

CINDER_APP_BASIC( DonkeyKongMain, RendererGl )
